<?php
/**
 * 数独生成类
 * @copyright (c) bbaa
 * Vesions V2.0
 * 生成算法思路来源:http://blog.csdn.net/liusaint1992/article/details/51147149
 * https://github.com/liusaint/games/blob/master/soduku/soduku.js移植
 */
class Sudoku {
	public $Sudoku_arr=Array();
	public $Sudoku_clone=Array();
	private $break=0;
	public function initarr() {
		$this->Sudoku_arr=Array();
		$this->Sudoku_clone=Array();
	}
	private function setThird($x,$y) {
		$numArr=array(1,2,3,4,5,6,7,8,9);
		shuffle($numArr);
		$thIndexArr =array(array($x-1,$y-1),array($x,$y-1),array($x+1,$y-1),array($x-1,$y),array($x,$y),array($x+1,$y),array($x-1,$y+1),array($x,$y+1),array($x+1,$y+1));
		for($a=0;$a<9;$a++){
			$this->Sudoku_arr[$thIndexArr[$a][0]][$thIndexArr[$a][1]]=$numArr[$a];
		}
	}
	private function GetXArr($j,$sdArr){
		//获取所在行的值。
		$arr = Array();
		for($a=0;$a<9;$a++){
			if(isset($this->Sudoku_arr[$a][$j])){
				$arr[]=$sdArr[$a][$j];
			}
		}
		return $arr;
	}
	private function GetYArr($i,$sdArr){
		//获取所在列的值。
		$arr = Array();
		for($a=0;$a<9;$a++){
			if(isset($sdArr[$i][$a])){
				$arr[]=$sdArr[$i][$a];
			}
		}
		return $arr;
	}
	private function GetThArr($i,$j,$sdArr){
		//获取所在三宫格的值。
		$arr = array();
		$cenNum = str_split((string)$this->getTh($i+1,$j+1));
		$x=$cenNum[0];
		$y=$cenNum[1];
		$thIndexArr = array(array($x-1,$y-1),array($x,$y-1),array($x+1,$y-1),array($x-1,$y),array($x,$y),array($x+1,$y),array($x-1,$y+1),array($x,$y+1),array($x+1,$y+1));
		for($a=0;$a<9;$a++){
			if(isset($sdArr[$thIndexArr[$a][0]][$thIndexArr[$a][1]])){
				$arr[]=$sdArr[$thIndexArr[$a][0]][$thIndexArr[$a][1]];
			}
		}
		return $arr;
	}
	private function getTh($i,$j){
		//获取所在三宫格的中间位坐标。
		$cenArr = array(11,41,71,14,44,74,17,47,77);
		$index = (ceil($j/3)-1) * 3 +ceil($i/3) -1;
		$cenNum = $cenArr[$index];
		return $cenNum;
	}
	private function arrMinus($arr1,$arr2){
		foreach($arr2 as $val) {
			Unset($arr1[(int)array_search($val,$arr1)]);
		}
		return $arr1;
	}
	public function GetSudoku_() {
		//重置数独
		$this->initarr();
		$this->setThird(1,1);
		$this->setThird(4,4);
		$this->setThird(7,7);
		$this->Sudoku_clone=$this->Sudoku_arr;
	}
	public function GetSudoku($self=false,$break=true) {
		//获取数独
		if(empty($this->Sudoku_clone)) {$this->GetSudoku_();} else {
			if (empty($this->Sudoku_arr)) {$this->Sudoku_arr=$this->Sudoku_clone;}
			if($break) {$this->break++;}
			if ($this->break>=15) {
				$this->break=0;
				unset($this->Sudoku_arr);
				$this->GetSudoku_();
			} else {
				unset($this->Sudoku_arr);
				$this->Sudoku_arr=$this->Sudoku_clone;
			}
		}
		$allNum = array(1,2,3,4,5,6,7,8,9);
		for($i=0;$i<9;$i++){
			for($j=0;$j<9;$j++){
				if(!isset($this->Sudoku_arr[$i][$j])||(int)$this->Sudoku_arr[$i][$j]==0) {
					$XArr = $this->getXArr($j,$this->Sudoku_arr);
					$YArr = $this->getYArr($i,$this->Sudoku_arr);
					$thArr = $this->getThArr($i,$j,$this->Sudoku_arr);
					$arr=array_unique(array_merge($XArr,$YArr,$thArr));
					unset($XArr,$YArr,$thArr);
					$ableArr=$this->arrMinus($allNum,$arr);
					unset($arr);
					shuffle($ableArr);
					if ((int)Count($ableArr)==0||empty($ableArr)||empty($ableArr[0])||$ableArr[0]=="") {unset($ableArr);$this->GetSudoku($self,$break);break;}
					$this->Sudoku_arr[$i][$j]=$ableArr[0];
					unset($ableArr);
				}
			}
		}
		/* for ($i=3;$i<9;$i++) {
			ksort($this->Sudoku_arr[$i]);
		} */
		if (!$self) { 
			return $this->Sudoku_arr;
		} else {
			return $this;
		}
	}
	
	public function toString($arr = array()) {
		//数独数组转字符串
		if(empty($arr)) {
			$arr=$this->Sudoku_arr;
		}
		$String='';
		for($i=0;$i<9;$i++){
			for($j=0;$j<9;$j++){
				$String.=$arr[$j][$i];
			}
		}
		return $String;
	}
	public function Randomc($arr,$Count=30) {
		//随机扣数
		for($Co=1;$Co<=$Count;$Co++) {
			$Ran=mt_rand(0,8);
			$Ran1=mt_rand(0,8);
			While ($arr[$Ran][$Ran1]==0) {
				$Ran=mt_rand(0,8);
				$Ran1=mt_rand(0,8);
			}
			$arr[$Ran][$Ran1]=0;
		}
		Return $arr;
	}
	public function toArr($str) {
		//数独字符串转数组
		$Arr=str_split($str,9);
		for($i=0;$i<=8;$i++){
			$Arr1[$i]=str_split($Arr[$i]);
		}
		return $this->YXArrToXYArr($Arr1);
	}
	public function check($arr) {
		//检查数独
		if (!is_array($arr)) {
			if (strlen($arr)!=81) {return false;}
			$arr=$this->toArr($arr);
		} else {
			if (count($arr)!=81) {return false;}
		}
		for($i=0;$i<9;$i++){
			$MathArrX=Array(1,2,3,4,5,6,7,8,9);
			$MathArrY=Array(1,2,3,4,5,6,7,8,9);
			for($j=0;$j<9;$j++){
				unset($MathArrX[(int)array_search($arr[$i][$j],$MathArrX)]);
				unset($MathArrY[(int)array_search($arr[$j][$i],$MathArrY)]);
			}
			if (Count($MathArrX)!=0|Count($MathArrX)!=0) {$error=true;return false;break;}
		}
		$cenArr = array(11,41,71,14,44,74,17,47,77);
		for($a=0;$a<9;$a++) {
			$ZJarr=Array();
			$ZJArray=Array(1,2,3,4,5,6,7,8,9);
			$cenNum=str_split($cenArr[$a]);
			$x=$cenNum[0];
			$y=$cenNum[1];
			$thIndexArr = array(array($x-1,$y-1),array($x,$y-1),array($x+1,$y-1),array($x-1,$y),array($x,$y),array($x+1,$y),array($x-1,$y+1),array($x,$y+1),array($x+1,$y+1));
			for($b=0;$b<9;$b++){
				if(isset($arr[$thIndexArr[$b][0]][$thIndexArr[$b][1]])){
					$ZJarr[]=$arr[$thIndexArr[$b][0]][$thIndexArr[$b][1]];
				}
			}
			for ($c=0;$c<9;$c++) {
				unset($ZJArray[(int)array_search($ZJarr[$c],$ZJArray)]);
			}
			if (Count($ZJArray)!=0) {$error=true;break;}
		}
		if ($error) {return false;} else {return true;}
	}
	public function diff_sudoku($Sudoku_arr,$Sudoku_arr1) {
		if(is_array($Sudoku_arr)&&is_array($Sudoku_arr1)&&isset($Sudoku_arr[11])) {
			$Sudoku_arr=self::toString($Sudoku_arr);
			$Sudoku_arr1=self::toString($Sudoku_arr1);
		}
		$Sudoku_arr=str_split($Sudoku_arr);
		$Sudoku_arr1=str_split($Sudoku_arr1);
		for ($i=0;$i<81;$i++) {
			if ($Sudoku_arr[$i]==$Sudoku_arr1[$i]||$Sudoku_arr1[$i]==0) {
				Unset ($Sudoku_arr[$i],$Sudoku_arr1[$i]);
			}
		}
		if (Count($Sudoku_arr)==0) {return true;} else {return false;}
	}
	public function YXArrToXYArr($Arr) {
		$Sd=Array();
		for($i=0;$i<9;$i++){
			for($j=0;$j<9;$j++){
				$Sd[$j][$i]=$Arr[$i][$j];
			}
		}
		return $Sd;
	}
}
